package com.ddxz.tool.sql.dynamic;

import com.alibaba.druid.pool.DruidDataSource;
import com.ddxz.tool.core.security.SecurityUtil;
import com.ddxz.tool.sql.struct.DataSourceInfo;
import lombok.Getter;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @description 动态数据源
 * @author user1
 * @date 2019年12月18日
 * @version 1.0.0
 */

public class DdxzDynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected DataSource determineTargetDataSource() {
        DataSourceInfo dsi = DdxzDataSourceContextHandler.getDataSource();
        logger.debug("use " + dsi.toString());

        if(DEFAULT_DS.equals(dsi.getKey())) {
            return defaultDatasource;
        }

        if(dataSourceMap.containsKey(dsi)) {
            return (DataSource)dataSourceMap.get(dsi);
        }

        try {
            DataSource ds = DataSourceBuilder.create()
                    .driverClassName(dbType.get(dsi.getDbtype()).getDriverClassName())
                    .type(DruidDataSource.class)
                    .url(String.format(dbType.get(dsi.getDbtype()).getUrl(), dsi.getHost(), dsi.getPort(), dsi.getInstance()))
                    .username(dsi.getUser())
                    .password(SecurityUtil.rsaDecode(dsi.getPassword())).build();
            ((DruidDataSource)ds).setValidationQuery(dbType.get(dsi.getDbtype()).getValidationQuery());

            dataSourceMap.put(dsi, ds);
            return ds;
        } catch (Exception e) {
            e.printStackTrace();
            return super.determineTargetDataSource();
        }
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DdxzDataSourceContextHandler.getDataSource();
    }

    private DataSource defaultDatasource;

    public void setDefaultDataSource(Object defaultDataSource) {
        super.setDefaultTargetDataSource(defaultDataSource);
        this.defaultDatasource = defaultDatasource;
    }

    public void setDataSources(Map<Object, Object> dataSourceMap) {
        this.dataSourceMap = dataSourceMap;
        super.setTargetDataSources(dataSourceMap);
        DdxzDataSourceContextHandler.addDataSourceKey(dataSourceMap.keySet());
    }

    private Map<Object, Object> dataSourceMap;
    private static Map<Integer, CasicDatabaseConfig> dbType = new HashMap<>(4);
    private static final String DEFAULT_DS = "major";

    static {
        // 添加MySQL数据库连接信息
        dbType.put(2, new CasicDatabaseConfig("jdbc:mysql://%s:%d/%s?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai",
                "com.mysql.cj.jdbc.Driver"
                , "select 1"));
        // 添加PostgreSQL数据库连接信息
        dbType.put(0, new CasicDatabaseConfig("jdbc:postgresql://%s:%d/%s?allowMultiQueries=true&autoReconnect=true",
                "org.postgresql.Driver",
                "select 1"));
        // 添加Oracle 数据库连接信息
        dbType.put(1, new CasicDatabaseConfig("jdbc:oracle:thin:@//%s:%d",
                "oracle.jdbc.driver.OracleDriver",
                "select 1 from dual"));
        // 添加SqlServer数据库连接信息
        dbType.put(3, new CasicDatabaseConfig("jdbc:sqlserver://%s:%d;DatabaseName=%s",
                "com.microsoft.sqlserver.jdbc.SQLServerDriver",
                "select 1"));
        // 添加Hive数据库连接信息
        dbType.put(4, new CasicDatabaseConfig("jdbc:hive2://%s:%d/%s",
                "org.apache.hive.jdbc.HiveDriver",
                "select 1"));
        // 添加达梦数据库连接信息
        dbType.put(5, new CasicDatabaseConfig("jdbc:dm://%s:%d",
                "dm.jdbc.driver.DmDriver",
                "select 1 from dual"));
    }

    @Getter
    static class CasicDatabaseConfig {
        private String url;
        private String driverClassName;
        private String validationQuery;

        public CasicDatabaseConfig(String url, String driverClassName, String validationQuery) {
            this.url = url;
            this.driverClassName = driverClassName;
            this.validationQuery = validationQuery;
        }
    }
}
